package jrobutt.room.level;

import jrobutt.*;

import java.awt.*;
import java.util.*;
import java.util.List;

public class TerrainGrid extends GameObject
{
    public static final Map<Integer, GameSprite> sprite_cache;
    static
    {
        sprite_cache = new HashMap<Integer, GameSprite>();
    }
    
    public static final int ENTRANCE_GATE_DATUM = 2;
    public static final int EXIT_GATE_DATUM = 3;
    public static final int GOAL_DATUM = 4;
    
    public final int width;
    public final int height;
    public final Block[] blocks;
    public final float scale;
    
    private Map<Point, Block> entrance_gate;
    private Map<Point, Block> exit_gate;
    
    private GameSprite sprite_of (int datum)
    {
        GameSprite sprite = sprite_cache.get(datum);
        if (sprite != null) return sprite;
        
        Image image = Global.image_loader.load("terrain/" + datum + ".png");
        sprite = new GameSprite(image);
        sprite_cache.put(datum, sprite);
        return sprite;
    }
    
    private int flat_index (int x, int y)
    {
        return width * y + x;
    }
    private boolean is_legal (int x, int y)
    {
        return x >= 0 && x < width && y >= 0 && y < height;
    }
    
    public class Block extends GameObject
    {
        public final int datum;
        
        public Block (int xx, int yy, int datum)
        {
            super(xx * scale, yy * scale, sprite_of(datum));
            this.datum = datum;
        }
        
        public float getWidth ()
        {
            return scale;
        }
        public float getHeight ()
        {
            return scale;
        }
    }
    
    private ArrayList<Block> cached_retval_list;
    
    public TerrainGrid (LevelData data, float scale)
    {
        super(0.0f, 0.0f, null);
        
        this.width = data.width;
        this.height = data.height;
        this.scale = scale;
        
        int[] block_data = data.block_data;
        
        this.blocks = new Block[width*height];
        this.entrance_gate = new HashMap<Point, Block>();
        this.exit_gate = new HashMap<Point, Block>();
        for (int k=0; k<block_data.length; k++)
        {
            int datum = block_data[k];
            if (datum == 0) continue;
            
            int block_x = k % this.width;
            int block_y = k / this.width;
            Block block = new Block(block_x, block_y, datum);
            if (datum == ENTRANCE_GATE_DATUM)
            {
                entrance_gate.put(new Point(block_x, block_y), block);
            }
            else if (datum == EXIT_GATE_DATUM)
            {
                exit_gate.put(new Point(block_x, block_y), block);
            }
            else
            {
                blocks[k] = block;
            }
        }
        
        cached_retval_list = new ArrayList<Block>(4);
    }
    
    public List<Block> get_blocks_within (float x, float y, float width, float height)
    {
        cached_retval_list.clear();
        
        int min_x = (int)Math.floor(x / this.scale);
        int max_x = (int)Math.ceil((x + width) / this.scale) - 1;
        int min_y = (int)Math.floor(y / this.scale);
        int max_y = (int)Math.ceil((y + height) / this.scale) - 1;
        for (int yy=min_y; yy<=max_y; yy++) for (int xx=min_x; xx<=max_x; xx++)
        {
            if (!is_legal(xx, yy)) continue;
            Block block = blocks[flat_index(xx, yy)];
            if (block == null) continue;
            
            cached_retval_list.add(block);
        }
        
        return cached_retval_list;
    }
    
    public void close_entrance_gate ()
    {
        for (Map.Entry<Point, Block> pair : entrance_gate.entrySet())
        {
            Point p = pair.getKey();
            Block block = pair.getValue();
            
            int k = flat_index(p.x, p.y);
            
            int datum = ENTRANCE_GATE_DATUM;
            block = new Block(p.x, p.y, datum);
            blocks[k] = block;
        }
        entrance_gate.clear();
    }
    public void close_exit_gate ()
    {
        for (Map.Entry<Point, Block> pair : exit_gate.entrySet())
        {
            Point p = pair.getKey();
            Block block = pair.getValue();
            
            int k = flat_index(p.x, p.y);
            
            int datum = EXIT_GATE_DATUM;
            block = new Block(p.x, p.y, datum);
            blocks[k] = block;
        }
        exit_gate.clear();
        
        for (int k=0; k<blocks.length; k++)
        {
            Block block = blocks[k];
            if (block == null) continue;
            
            if (block.datum == GOAL_DATUM)
            {
                blocks[k] = null;
            }
        }
    }
    
    public void udpate ()
    {
        for (Block block : blocks) if (block != null) block.update();
    }
    public void render (Graphics2D g)
    {
        for (Block block : blocks) if (block != null) block.render(g);
    }
}
